#pragma once

#include "TableSchema.h"
#include "string"
#include "string_view"
#include "filesystem"
#include "Table.h"
#include "json/value.h"
#include <filesystem>

class Database 
{
private:
    std::filesystem::path path_;

public:
    Database(std::string_view path): path_(path) 
    {
        if (!std::filesystem::exists(path_)) {
            std::filesystem::create_directories(path_);
        }
    }

    void createTable(const TableSchema& schema)
    {
        if (existTable(schema.name())) {
            throw std::runtime_error("Table already exists");   
        }

        createTableMetaDataInfo(schema);
        createTableDataStorage(schema);
    }

    Table openTable(std::string_view table_name) 
    {
        return Table(path_, table_name);
    }

    bool existTable(std::string_view table_name)
    {
        std::filesystem::path schema_path = path_ / (std::string(table_name) + ".schema");
        return std::filesystem::exists(schema_path);
    }

private:
    void createTableMetaDataInfo(const TableSchema& schema) 
    {
        Json::Value root;
        root["name"] = schema.name();
        Json::Value colums_array(Json::arrayValue);
        for (size_t i = 0; i < schema.columnCount(); ++i) 
        {
            const auto column_def = schema.column(i);
            Json::Value column_object;
            column_object["name"] = column_def.name();
            column_object["type"] = columnTypeToString(column_def.type());
            colums_array.append(column_object);
        }
        root["columns"] = colums_array;

        std::filesystem::path schema_path = path_ / (schema.name() + ".schema");
        std::ofstream schema_file(schema_path);
        if (schema_file.is_open()) 
        {
            schema_file << root;
            schema_file.close();
        }
    }

    void createTableDataStorage(const TableSchema& schema)
    {
        std::filesystem::path data_path = path_ / (schema.name() + ".data");
        Json::Value data_root;
        data_root["rows"] = Json::Value(Json::arrayValue);
        std::ofstream data_file(data_path);
        if (data_file.is_open()) {
            data_file << data_root;
            data_file.close();
        }
    }
};